早安!
原本流程控制只想用一天的篇幅,但發現有好多內容可以寫的,
所以就想順勢擴充成3篇文章,
拆分文章的好處就是盡量讓clojure初學者好消化易吸收無負擔~
跟乾糧比起來
肉湯罐罐
好消化易吸收
今天就來解說第二part: when / while / doseq 吧!
第11天的文章在最後的例子稍稍帶到for
(for [x (range 1 6)]
(* x x))
(1 4 9 16 25 36)
第12天的農場舉一反三篇的for
有一個很好的例子:
(let [map {:cat 5 :dog 3 :chicken 15 :cow 22 :sheep 46 }]
(select-keys map (for [[key value] map :when (even? value)] key)))
=>{:cow 22, :sheep 46}
有沒有發現for
可以跟不同條件組合起來?
常見的有when / while。
接下來我們就先來看看之前有印象的when吧!
Usage: (when test & body)
Evaluates test. If logical true, evaluates body in an implicit do.
如果if條件符合,do 執行接下來的指令,但不像if一樣需要有else的條件
The when operator is like a combination of if and do, but with no else branch.
(nil? nil)
=> true
;;囉唆的另一種寫法: when
(when (nil? nil) true)
=> true
---
(empty? [])
=> true
;;囉唆的另一種寫法: when
(when (empty? []) true)
=> true
?囉唆的另一種寫法只是為了讓我們多練習一下when的括弧撰寫結構,小試身手而已啦!
when是(if .. do ..)的macro(巨集)
,可以用 macroexpand
來展開看原本的樣子:
(macroexpand '(when (empty? []) true))
=>(if (empty? []) (do true))
之後的鐵人賽也會介紹其他的macro,敬請期待歐!
舉一反三的結構長這樣:
(select-keys map
(for [[key value]
map
:when (even? value)]
key))
試著用最簡單的例子出發,vec裡1到6的element乘以3,符合偶數條件的再撈出來:
(for [x [1 2 3 4 5 6]
:let [y (* x 3)]
:when (even? y)]
y)
;;=> (6 12 18)
當然,如果y乘以的是偶數那就會全撈出來啦!
(for [x [1 2 3 4 5 6]
:let [y (* x 10)]
:when (even? y)]
y)
;;=> (10 20 30 40 50 60)
查了小抄之後,發現看來when的變形系列跟if一樣,也不少呢!
if
if-not
if-let
if-some
;;
when
when-not
when-let
when-some
when-first
來研究一下when變形怪之一:when-first
!
(when-first bindings & body)
(when-first [a [1 2 3]] a)
bindings => x xs
Roughly the same as
(when (seq xs) (let [x (first xs)] body))
but xs is evaluated only once
昨天有提到let可以做binding(綁定)的例子
(for [x (range 1 6)
:let [square-x (* x x)
cube-x (* x x x)]]
[x square-x cube-x])
=> ([1 1 1] [2 4 8] [3 9 27] [4 16 64] [5 25 125])
when-first
的第一個接的就是預設binding,不需特意使用let囉
來舉幾個例子試試看:
(when-first [first-element [1 2 3 4]] first-element)
=> 1
(when-first [first-element ["1 2 3 4"]] first-element)
=>"1 2 3 4"
(when-first [first-element ["1"]] first-element)
=> "1"
(when-first [first-element []] first-element)
=> nil
(while test & body)
Repeatedly executes body while test expression is true. Presumes
some side-effect will cause test to become false/nil. Returns nil
side-effect也是要在後面章節細談的東東。我們先來看例子就好:
從1開始遞增印出小於6的正數
(def a (atom 1))
(while ( < @a 6 )
(do (println @a)
(swap! a inc)))
=>
1
2
3
4
5
nil
也可以舉個反過來寫的例子:
印出由6往下遞減1,一直到大於0的正數
(def a (atom 5))
(while (pos? @a)
(println @a)
(swap! a dec))
5
4
3
2
1
nil
while最重要的是要有break loop的條件,
如果把dec改成inc
(def a (atom 5))
(while (pos? @a)
(println @a)
(swap! a inc))
=>...
...
...
你會聽到電腦風扇開始起飛,然後就stackoverflow啦 ^_^
同樣都是好用的迭代語法,我們來比較一下 when
和 while
說到stackoverflow,來看一看stackoverflow這篇討論提到:
:when
iterates over the bindings, but only evaluates the body of the loop when the condition is true
.
;;回傳除了5之外,1到10的數字
(for [x (range 1 11) :when (not= x 5)] x)
=> (1 2 3 4 6 7 8 9 10)
:while
iterates over the bindings and evaluates the body until the condition is false
;;回傳1到10之中小於5的數字
(for [x (range 1 11) :while (not= x 5)] x)
=> (1 2 3 4)
有沒有覺得以上的說明非常清晰易懂!
昨天講到for
可以超展開,
(for [x (range 1 6)
:let [square-x (* x x)
cube-x (* x x x)]]
[x square-x cube-x])
=> ([1 1 1] [2 4 8] [3 9 27] [4 16 64] [5 25 125])
今天也想提一下Flow Control另一個sequence base的function
doseq
做收尾
;;語法
(doseq seq-exprs & body)
Repeatedly executes body (presumably for side-effects)
withbindings and filtering as provided by "for".
Does not retain the head of the sequence.
Returns nil.
來用doseq
表達一下,Cartesian cross笛卡爾cross, 在集合論中(Set theory)代表所有可能的有序對組成的集合
(doseq [vec1 ["a" "b" "c"]
vec2 ["x" "y" "z"]]
(println vec1 vec2))
=>
a x
a y
a z
b x
b y
b z
c x
c y
c z
nil
短短的三行就成功印出超展開的九宮格(?)
有沒有感受到clojure語法可以簡單實作迭代的威力呀?
明天來討論另外三個我覺得想推薦給大家,
在條件/流程控制的常用function :D
case
cond
doall